/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.util.core;

import org.apache.commons.collections.list.SetUniqueList;
import org.apache.commons.digester.Digester;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXException;

import javax.servlet.ServletContext;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * <p>Provides common functions to operate with resource types specified in XML file</p>
 * This class implements Singleton design pattern
 * <p><a href="ResourceTypesManager.java.html"><i>View Source</i></a></p>
 *
 * @author Andrey Grebnev <a href="mailto:andrey.grebnev@blandware.com">&lt;andrey.grebnev@blandware.com&gt;</a>
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.11 $ $Date: 2006/08/03 10:08:27 $
 */
public class ResourceTypesManager {

	private static final String RESOURCE_TYPES_FILE = "/WEB-INF/resource-types.xml";
	public static final String INSTANCE_KEY = "com.blandware.atleap.ResourceTypesManager.INSTANCE";

	protected transient final Log log = LogFactory.getLog(ResourceTypesManager.class);

	protected Map resourceTypes = new HashMap();

	/**
	 * Private constructor
     *
     * @param servletContext Servlet context
	 */
	private ResourceTypesManager(ServletContext servletContext) {
		InputStream is = servletContext.getResourceAsStream(RESOURCE_TYPES_FILE);
		if ( is == null ) {
			if ( log.isErrorEnabled() ) {
				log.error("Could not locate xml file with resource types: " + RESOURCE_TYPES_FILE);
			}
		}
		try {

			Digester digester = new Digester();
			digester.push(this);
			digester.setNamespaceAware(false);
			digester.setValidating(false);

			digester.addObjectCreate("resourceTypes/resourceType", ResourceType.class);
			digester.addSetProperties("resourceTypes/resourceType");
			digester.addCallMethod("resourceTypes/resourceType/mimeType", "addMimeType", 1);
			digester.addCallParam("resourceTypes/resourceType/mimeType", 0, "identifier");
			digester.addCallMethod("resourceTypes/resourceType/mimeType/extension", "addExtension", 1);
			digester.addCallParam("resourceTypes/resourceType/mimeType/extension", 0);
			digester.addSetNext("resourceTypes/resourceType", "addResourceType", ResourceType.class.getName());
			digester.parse(is);

		} catch ( IOException e ) {
			if ( log.isErrorEnabled() ) {
				log.error("Could not load xml file with resource types: " + RESOURCE_TYPES_FILE);
			}
		} catch ( SAXException e ) {
			if ( log.isErrorEnabled() ) {
				log.error("Could not parse xml file with resource types: " + RESOURCE_TYPES_FILE);
			}
		}
	}


	/**
	 * Returns instance of ResourceTypesManager
	 *
     * @param servletContext The servlet context
	 * @return Instance of ResourceTypesManager
	 */
	public static ResourceTypesManager getInstance(ServletContext servletContext) {
		ResourceTypesManager ourInstance = (ResourceTypesManager) servletContext.getAttribute(INSTANCE_KEY);
		if ( ourInstance == null ) {
			ourInstance = new ResourceTypesManager(servletContext);
			servletContext.setAttribute(INSTANCE_KEY, ourInstance);
		}
		return ourInstance;
	}

	/**
	 * Gets resource type identifier by extension
	 *
	 * @param extension Extension to search by
	 * @return resource type identifier
	 */
	public String getResourceTypeByExtension(String extension) {
		if ( log.isDebugEnabled() ) {
			log.debug("Get resource type identifier by extension " + extension);
		}

		for ( Iterator i = resourceTypes.values().iterator(); i.hasNext(); ) {
			ResourceType resourceType = (ResourceType) i.next();
			if ( resourceType.isValidExtension(extension) ) {
				return resourceType.getIdentifier();
			}
		}

		return null;
	}

	/**
	 * Gets resource type identifier by file name
	 *
	 * @param filename Name of file to find resource by
	 * @return resource type identifier
	 */
	public String getResourceTypeByFileName(String filename) {
        if ( log.isDebugEnabled() ) {
            log.debug("Get resource type by file name " + filename);
        }

        // TODO: client separator char may differ from our one, so check this situation
//		int slash = filename.lastIndexOf(File.separatorChar);
		int period = filename.lastIndexOf(".");
		if ( (period >= 0) /*&& (period > slash)*/ ) {
			String extension = filename.substring(period + 1, filename.length());
			return getResourceTypeByExtension(extension);
		} else {
			return null;
		}
	}

	/**
	 * Gets MIME type of resource by extension
	 *
	 * @param extension Extension to search by
	 * @return MIME type
	 */
	public String getMimeTypeByExtension(String extension) {
		if ( log.isDebugEnabled() ) {
			log.debug("Get MIME type identifier by extension " + extension);
		}

		for ( Iterator i = resourceTypes.values().iterator(); i.hasNext(); ) {
			ResourceType resourceType = (ResourceType) i.next();
			Map mimeTypes = resourceType.getMimeTypes();
			for ( Iterator j = mimeTypes.entrySet().iterator(); j.hasNext(); ) {
				Map.Entry entry = (Map.Entry) j.next();
				List extensions = (List) entry.getValue();
				if ( extensions.contains(extension.toLowerCase()) ) {
					return (String) entry.getKey();
				}
			}
		}

		return null;
	}

	/**
	 * Gets MIME type by file name
	 *
	 * @param filename name of file for which to determine its MIME type
	 * @return MIME type
	 */
	public String getMimeTypeByFileName(String filename) {
        if ( log.isDebugEnabled() ) {
            log.debug("Get MIME type by file name " + filename);
        }

//		int slash = filename.lastIndexOf(File.separatorChar);
		int period = filename.lastIndexOf(".");
		if ( (period >= 0) /*&& (period > slash)*/ ) {
			String extension = filename.substring(period + 1, filename.length());
			return getMimeTypeByExtension(extension);
		} else {
			return null;
		}
	}

	/**
	 * Gets full list of all supported extensions
	 *
	 * @return list of strings
	 */
	public List getSupportedExtensions() {
		List extList = new ArrayList();
		for ( Iterator i = resourceTypes.values().iterator(); i.hasNext(); ) {
			ResourceType resourceType = (ResourceType) i.next();
			extList.addAll(resourceType.getSupportedExtensions());
		}
		return extList;
	}

	/**
	 * Gets list of resource type extensions
	 *
	 * @param resourceTypeIdentifier identifier of resource type
	 * @return list of extensions
	 */
	public List getResourceTypeExtensions(String resourceTypeIdentifier) {
		ResourceType resourceType = (ResourceType) resourceTypes.get(resourceTypeIdentifier);
		if ( resourceType != null ) {
			return resourceType.getSupportedExtensions();
		} else {
			return null;
		}
	}

	/**
	 * Gets default MIME type identifier for specified resource type
	 *
	 * @param resourceTypeIdentifier identifier of resource type
	 * @return MIME type
	 */
	public String getDefaultMimeType(String resourceTypeIdentifier) {
		ResourceType resourceType = (ResourceType) resourceTypes.get(resourceTypeIdentifier);
		if ( resourceType != null ) {
			return resourceType.getDefaultMimeType();
		} else {
			return null;
		}
	}

	/**
	 * Gets default extension for specified MIME type
	 *
	 * @param mimeTypeIdentifier identifier of MIME type
	 * @return extension
	 */
	public String getDefaultExtension(String mimeTypeIdentifier) {
		String defaultExtension = null;
		for ( Iterator i = resourceTypes.values().iterator(); i.hasNext() && defaultExtension == null; ) {
			ResourceType resourceType = (ResourceType) i.next();
			defaultExtension = resourceType.getDefaultExtension(mimeTypeIdentifier);
		}
		return defaultExtension;
	}

	/**
	 * Gets resource type name key from i18n bundle
	 *
	 * @param resourceTypeIdentifier identifier of resource type
	 * @return key from bundle
	 */
	public String getResourceTypeNameKey(String resourceTypeIdentifier) {
		ResourceType resourceType = (ResourceType) resourceTypes.get(resourceTypeIdentifier);
		if ( resourceType != null ) {
			return resourceType.getNameKey();
		} else {
			return null;
		}
	}

	/**
	 * Registers resource type
	 *
	 * @param resourceType Resource type to register
	 */
	public void addResourceType(ResourceType resourceType) {
		resourceTypes.put(resourceType.getIdentifier(), resourceType);
	}

	/**
	 * Inner class that represents resource type
	 */
	public static class ResourceType {

		/**
		 * Identifier of resource type
		 */
		protected String identifier;

		/**
		 * I18n bundle key for type name
		 */
		protected String nameKey;

		/**
		 * List of mappings mimeType -&gt; extensions
		 */
		protected Map mimeTypes = new HashMap();

		/**
		 * Default MIME type
		 */
		protected String defautMimeType = null;

		/**
		 * List of extensions
		 */
		protected List extensions = SetUniqueList.decorate(new ArrayList());

		/**
		 * Creates new instance of resource type
		 */
		public ResourceType() {
		}

		/**
		 * Creates new instance of resource type with specified identifier
		 *
		 * @param identifier Identifier of this resource type
		 */
		public ResourceType(String identifier) {
			this.identifier = identifier;
		}

		/**
		 * Creates new instance of resource type with specified identifier and name key
		 *
		 * @param identifier Identifier of this resource type
		 * @param nameKey    Name key for this resource type
		 */
		public ResourceType(String identifier, String nameKey) {
			this.identifier = identifier;
			this.nameKey = nameKey;
		}

		/**
		 * Returns identifier of this resource type
		 *
		 * @return Identifier
		 */
		public String getIdentifier() {
			return identifier;
		}

		/**
		 * Sets identifier of this resource type
		 *
		 * @param identifier New identifier
		 */
		public void setIdentifier(String identifier) {
			this.identifier = identifier;
		}

		/**
		 * Returns name key for this resource type
		 *
		 * @return Name key
		 */
		public String getNameKey() {
			return nameKey;
		}

		/**
		 * Sets new name key for this resource type
		 *
		 * @param nameKey New name key
		 */
		public void setNameKey(String nameKey) {
			this.nameKey = nameKey;
		}

		/**
		 * Returns list of mappings of MIME type to list of extensions
		 *
		 * @return List of mappings of MIME types to list of extensions
		 */
		public Map getMimeTypes() {
			return mimeTypes;
		}

		/**
		 * Sets list of mappings of MIME type to list of extensions
		 *
		 * @param mimeTypes New list of mappings
		 */
		public void setMimeTypes(Map mimeTypes) {
			this.mimeTypes = mimeTypes;
		}

		/**
		 * Returns list of extensions supported in this resource type
		 *
		 * @return List of supported extensions
		 */
		public List getSupportedExtensions() {
			SetUniqueList extSet = SetUniqueList.decorate(new ArrayList());
			for ( Iterator i = mimeTypes.values().iterator(); i.hasNext(); ) {
				SetUniqueList extensions = (SetUniqueList) i.next();
				extSet.addAll(extensions);
			}
			return extSet;
		}

		/**
		 * Returns true if resource of this type can have specified extension
		 *
		 * @param extension Extension to check
		 * @return <code>true</code> if resource of this type can have specified extension
		 */
		public boolean isValidExtension(String extension) {
			return getSupportedExtensions().contains(extension.toLowerCase());
		}

		/**
		 * Returns default MIME type of this resource type
		 *
		 * @return default MIME type
		 */
		public String getDefaultMimeType() {
			return defautMimeType;
		}

		/**
		 * Adds MIME type with empty set of extensions
		 *
		 * @param identifier Identifier of MIME type to add
		 */
		public void addMimeType(String identifier) {
			if ( defautMimeType == null ) {
				defautMimeType = identifier;
			}
			if ( !mimeTypes.containsKey(identifier) ) {
				mimeTypes.put(identifier, extensions);
				extensions = SetUniqueList.decorate(new ArrayList());
			}
		}

		/**
		 * Adds extension
		 *
		 * @param extension Extension to add
		 */
		public void addExtension(String extension) {
			if ( extension != null ) {
				extensions.add(extension.toLowerCase());
			}
		}

		/**
		 * Returns default extensions for specified MIME type
		 *
		 * @param mimeTypeIdentifier Identifier of MIME type to get default extension for
		 * @return default extension for MIME type with specified identifier
		 */
		public String getDefaultExtension(String mimeTypeIdentifier) {
			SetUniqueList extensions = (SetUniqueList) mimeTypes.get(mimeTypeIdentifier);
			if ( extensions != null && !extensions.isEmpty() ) {
				return (String) extensions.get(0);
			} else {
				return null;
			}
		}

	}


}
